LK

Автор: Максим Сохацький



Вступ до Маленького Ядра

Little Kernel (LK) — це вбудоване ядро з відкритим вихідним кодом, яке використовується в різних операційних системах і продуктах, переважно в тих, що пов’язані з низькорівневими вбудованими рішеннями, авантажувачами та безпечними середовищами. Ось список операційних систем і продуктів, які використовують LK, заснований на доступній інформації та логічних висновках із контексту проєкту LK:

Тревіс Гейзельбрехт є ключовою фігурою у створенні Little Kernel (LK) — легкого вбудованого ядра з відкритим вихідним кодом, яке стало основою для багатьох проєктів, зокрема Zircon у Fuchsia OS від Google. Він розробив LK з нуля, спираючись на свій багатий досвід роботи над операційними системами, такими як BeOS, NewOS і низькорівневими компонентами для Danger Hiptop, Apple (iPhone) та інших платформ. Гейзельбрехт створив LK як модульне, портативне ядро для вбудованих систем, зосередившись на ефективності (розмір ядра лише 15-20 КБ на ARM), підтримці багатопоточності, управлінні пам’яттю та гнучкості для різних архітектур, таких як ARM, RISC-V і x86. Його підхід до дизайну ядра відображає прагнення до простоти та продуктивності, що зробило LK популярним вибором для завантажувачів і TEE у пристроях на базі Android та інших системах.

ОС

Перелік операційних систем, що використовують або базуються на LK:

Zircon (Fuchsia OS)

Zircon — це мікроядро, яке лежить в основі операційної системи Fuchsia від Google. Воно базується на LK і було значно розширене для потреб Fuchsia. LK слугує основою для реалізації базових функцій, таких як управління потоками, пам’яттю та апаратними абстракціями. Використання: Zircon використовується в Fuchsia, яка розроблена як альтернатива Android для пристроїв IoT, смартфонів і комп’ютерів.

Trusted Little Kernel (TLK)

TLK — це відкрита реалізація технології NVIDIA для створення надійного виконання (Trusted Execution Environment, TEE) на базі LK. Вона призначена для роботи з процесорами NVIDIA Tegra. Використання: TLK інтегрується з Trusted Firmware-A (TF-A) для забезпечення безпечного виконання на SoC NVIDIA Tegra, наприклад, у пристроях із підтримкою DRM (Digital Rights Management).

Android Bootloaders

LK широко використовується як основа для завантажувачів (bootloaders) у багатьох пристроях на базі Android. Завантажувач на базі LK запускає ядро Linux і забезпечує базові функції, такі як розблокування пристрою та вибір режимів завантаження. Використання: Приклади включають пристрої від Qualcomm (наприклад, Lumia з MSM8227), Fairphone (раніші моделі), і численні Android-пристрої з чипами від різних виробників.

Trusted Execution Environment (TEE) у Android

У сучасних Android-пристроях LK часто використовується для створення TEE поряд із ядром Linux. TEE забезпечує ізольоване середовище для таких функцій, як DRM, безпечні платежі та криптографічні операції. Використання: Реалізовано в пристроях із чипами Qualcomm, MediaTek тощо, де LK адаптовано під конкретні SoC.

Платформи

Qualcomm-based Devices

LK використовується в завантажувачах для пристроїв із процесорами Qualcomm Snapdragon. Наприклад, проєкт Android4Lumia адаптував LK для Lumia на базі MSM8227. Приклад: Nokia Lumia (деякі моделі).

NXP i.MX8

NXP має форк LK (littlekernel-imx8), який адаптовано для процесорів i.MX8. Це ядро використовується в низькорівневих задачах для вбудованих систем. Використання: Промислові системи, автомобільні рішення.

Raspberry Pi Pico

LK підтримує проєкти для Raspberry Pi Pico (на базі RP2040). Хоча це не операційна система, LK може бути зібрано для запуску простих тестових програм або як основа для RTOS на цій платформі. Використання: Освітні проєкти, DIY-розробка.

Fairphone

Ранні моделі Fairphone використовували LK як завантажувач. Джерельний код для LK доступний у FairphoneMirrors на GitHub. Використання: Завантажувач для Android у Fairphone 1, 2 тощо.

Експериментальні RTOS

LK часто застосовується як основа для створення кастомних операційних систем реального часу (RTOS) у вбудованих проєктах завдяки його легкості (15-20 КБ на ARM) та модульності. QEMU Емуляція: LK підтримує тестування в QEMU для різних архітектур (ARM, RISC-V, ARM64), що робить його популярним для розробки та навчання.

Інсталяція

LK підтримує широкий сперкт платформ:

$ make list make[1]: Entering directory '/home/tonpa/depot/bitedits/lk' List of all buildable projects: (look in project/ directory) armemu-test bananapi-f3-test dartuinoP0-bootloader dartuinoP0-test helio-test lm3s6965evb-test lpcexpresso1549-test lpclink2-lpcboot lpclink2-mdebug lpcxpresso4337-test mt6735 mt6797 nrf51-pca10000-test nrf51-pca10028-test nrf52-pca10040-test nrf52-pca10056-test nucleo-f072rb or1ksim pc-x86-64-test pc-x86-legacy-test pc-x86-test pico-test qemu-m4-test qemu-microblaze-test qemu-mips-test qemu-sifive-u-test qemu-virt-arm32-minimal qemu-virt-arm32-test qemu-virt-arm64-test qemu-virt-m68k-test qemu-virt-riscv32-test qemu-virt-riscv64-supervisor-test qemu-virt-riscv64-test rosco-m68k-test rpi2-test rpi3-test sifive-e-test sifive-unleashed-test stellaris-launchpad-test stm32-h103-test stm32-p107-test stm32-p407-test stm3220g-eval stm32746g-eval2-test stm32f4-discovery-test stm32f429i-disco-test stm32f746g-disco-test uzed-bootloader uzed-dram-test uzed-test vim2-test visionfive2-test zybo-dram-test zybo-microblaze-test zybo-test make[1]: Leaving directory '/home/tonpa/depot/bitedits/lk'

Для прикладу візьмемо найпопулярнішу мобільну платформу ARM64.

$ sudo apt install lld llvm gcc-aarch64-linux-gnu \ gcc-arm-none-eabi git build-essential gcc g++ \ make qemu-system-arm $ gmake qemu-virt-arm64-test \ 'CC=/usr/bin/clang --target=aarch64-unknown-elf' \ 'CPP=/usr/bin/clang-cpp --target=aarch64-unknown-elf' \ 'CXX=/usr/bin/clang++ --target=aarch64-unknown-elf' \ 'LD=/usr/bin/ld.lld' \ TOOLCHAIN_PREFIX=/usr/bin/llvm- \ CPPFILT=/usr/bin/llvm-cxxfilt

Запуск

$ ./scripts/do-qemuarm qemu-system-arm -cpu cortex-a15 -m 512 -smp 1 -machine virt,highmem=off -kernel build-qemu-virt-arm32-test/lk.elf -net none -nographic Generic timer initialized with freq 62500000 Hz, irq 27 FDT: found memory bank range [0x40000000, 0x5fffffff] (length 0x20000000) FDT: reserving physical range for FDT: [0x40000000, 0x400fffff] welcome to lk/MP boot args 0x0 0x0 0x0 0x0 INIT: cpu 0, calling hook 0x8011fab9 (version) at level 0x3ffff, flags 0x1 version: arch: arm platform: qemu-virt-arm target: qemu-virt-arm project: qemu-virt-arm32-test buildid: O3S23_LOCAL INIT: cpu 0, calling hook 0x801216ad (vm_preheap) at level 0x3ffff, flags 0x1 initializing heap calling constructors INIT: cpu 0, calling hook 0x801216f5 (vm) at level 0x4ffff, flags 0x1 initializing mp initializing threads initializing timers initializing ports creating bootstrap completion thread top of bootstrap2() INIT: cpu 0, calling hook 0x8011c6c1 (minip) at level 0x70000, flags 0x1 INIT: cpu 0, calling hook 0x8011d06d (pktbuf) at level 0x70000, flags 0x1 pktbuf: creating 256 pktbuf entries of size 1536 (total 393216) INIT: cpu 0, calling hook 0x80120489 (virtio) at level 0x70000, flags 0x1 releasing 0 secondary cpus initializing platform FDT: found 1 cpu PCIE: initializing pcie with ecam at 0x3f000000 found in FDT PCI: pci ecam functions installed PCI: last pci bus is 15 PCI dump: bus 0 dev 0000:00:00.0 vid:pid 1b36:0008 base:sub:intr 6:0:0 PCI dump post assign: bus 0 dev 0000:00:00.0 vid:pid 1b36:0008 base:sub:intr 6:0:0 initializing target INIT: cpu 0, calling hook 0x801203d9 (e1000) at level 0x90001, flags 0x1 initializing apps starting app inetsrv starting internet servers starting app shell entering main console loop ] ] help command list by block: [aes_test] aes_test : test AES encryption aes_bench : bench AES encryption [app] app : commands to operate on apps start : shortcut for app start [bio] bio : block io debug commands [cache_tests] cache_tests : test/bench the cpu cache [console] help : this list test : test the command processor history : command history repeat : repeats command multiple times [crc] crc16 : crc16 crc32 : crc32 adler32 : adler32 bench_cksum : benchmark the checksum routines [dcc] dcc : dcc stuff [float_tests] float_tests : floating point test [fs] fs : fs debug commands [fs_shell] ls : dir listing cd : change dir pwd : print working dir mkdir : make dir mkfile : make file rm : remove file stat : stat file cat : cat file df : list mounts [gfx] gfx : gfx commands [heap] heap : heap debug commands [kernel] threads : list kernel threads threadstats : thread level statistics threadload : toggle thread load display [mem] dw : display memory in words dh : display memory in halfwords db : display memory in bytes mw : modify word of memory mh : modify halfword of memory mb : modify byte of memory fw : fill range of memory by word fh : fill range of memory by halfword fb : fill range of memory by byte mc : copy a range of memory crash : intentionally crash panic : intentionally panic stackstomp : intentionally overrun the stack mtest : simple memory test chain : chain load another binary sleep : sleep number of seconds sleepm : sleep number of milliseconds time : print current time timeh : print current time hires [mem_tests] mem_test : test memory [minip] arp : arp commands mi : minip commands [page_alloc] page_alloc : page allocator debug commands [pcitests] pci : pci toolbox [platform_power] reboot : soft reset poweroff : powerdown [pmm] pmm : physical memory manager [psci] psci_version : show psci version [spifs] spifs : commands related to the spifs implementation. [stringtests] string : memcpy tests [tcp] tcp : tcp commands [tests] printf_tests : test printf printf_tests_float: test printf with floating point thread_tests : test the scheduler port_tests : test the ports clock_tests : test clocks bench : miscellaneous benchmarks fibo : threaded fibonacci spinner : create a spinning thread cbuf_tests : test lib/cbuf v9p_tests : test dev/virtio/9p v9fs_tests : test lib/fs/9p [unit_tests] ut : run some or all of the unit tests [version] version : print version [vm] vm : vm commands [vmm] vmm : virtual memory manager ] ] bench took 43866 cycles overhead to loop 1024 times took 194670812 cycles to memset a buffer of size 1048576 1024 times (1073741824 bytes), 5.515680 bytes/cycle took 166825809 cycles to memcpy a buffer of size 524288 1024 times (536870912 source bytes), 3.218153 source bytes/cycle took 1528583921 cycles to manually clear a buffer using wordsize 1 of size 1048576 1024 times (1073741824 bytes), 0.702442 bytes/cycle took 770214353 cycles to manually clear a buffer using wordsize 2 of size 1048576 1024 times (1073741824 bytes), 1.394082 bytes/cycle took 393369633 cycles to manually clear a buffer using wordsize 4 of size 1048576 1024 times (1073741824 bytes), 2.729600 bytes/cycle took 202183535 cycles to manually clear a buffer using wordsize 8 of size 1048576 1024 times (1073741824 bytes), 5.310728 bytes/cycle took 176413961 cycles to manually clear a buffer of size 1048576 1024 times 8 words at a time (1073741824 bytes), 6.086490 bytes/cycle took 183002285 cycles to manually clear a buffer of size 1048576 1024 times 8 words at a time using stm (1073741824 bytes), 5.867368 bytes/cycle touching the floating point unit took 144615 cycles for sin() took 139215 cycles for cos() took 103318 cycles for sinf() took 100141 cycles for cosf() took 16962 cycles for sqrt() took 133675 cycles for sqrtf() ]

Висновки

Портування AtomVM на LK

Так, AtomVM можна розмістити на базі Little Kernel (LK) за умови певних зусиль з інтеграції. LK надає необхідні базові компоненти — середовище виконання, управління пам’яттю, підтримку потоків і доступ до апаратного забезпечення — для роботи AtomVM. Розпишемо детально процес: 1) Компіляція AtomVM як додатка або модуля LK; 2) Налаштування пам’яті та потоків LK для підтримки потреб AtomVM; 3) Прив’язку апаратних взаємодій AtomVM до платформного рівня LK; 4) Тестування об’єднаної системи на цільовому пристрої (наприклад, ESP32 або емульованій платформі QEMU). Ця комбінація використовує легкі можливості ядра LK та модель високорівневої конкурентності AtomVM, створюючи потужне вбудоване середовище для додатків на Erlang/Elixir. Наприклад, пристрій Інтернету речей (IoT) може використовувати LK для ініціалізації апаратного забезпечення, а потім запускати AtomVM для керування мережевими датчиками за допомогою моделі акторів Erlang.

Практичні міркування

Зусилля з розробки: Портизація AtomVM на LK є нетривіальним, але досяжним завданням, яке, ймовірно, потребуватиме кількох тижнів роботи досвідченого розробника вбудованих систем, знайомого з обома платформами.
Сценарій використання: Така конфігурація підходить для додатків, які потребують конкурентності Erlang на мінімальному апаратному забезпеченні, наприклад, для IoT-вузлів або спеціалізованих завантажувачів, де простота LK і абстракція AtomVM є перевагами.
Альтернативи: Для пристроїв, які вже підтримуються AtomVM (наприклад, ESP32 з ESP-IDF), використання AtomVM самостійно може бути простішим. LK додає цінність у спеціалізованих або безMMU-сценаріях.
Підсумовуючи, розміщення AtomVM на LK є технічно можливим і відповідає цілям обох систем щодо легкого вбудованого виконання. Це потребуватиме інтеграції, але може відкрити унікальні можливості для розробки вбудованих систем на Erlang.

Можливість застосування як альтернативи Ada для сертифікованого середовища

Ada є мовою програмування, широко використовуваною в сертифікованих системах (наприклад, у авіації, обороні та медицині) завдяки її сильній типізації, вбудованій обробці винятків і здатності забезпечувати надійність на рівні коду. Однак Ada часто потребує складного середовища виконання (runtime), що може бути громіздким для вбудованих систем із обмеженими ресурсами. Комбінація LK і AtomVM може слугувати легшою альтернативою, поєднуючи простоту LK із надійністю моделі Erlang, яка базується на принципах обробки помилок, описаних у дисертації Джо Армстронга "Making reliable distributed systems in the presence of software errors" (2003).

Переваги LK+AtomVM як альтернативи Ada

Надійність і обробка помилок: У своїй дисертації Армстронг наголошує на концепції "let it crash" (дозволь системі впасти), де помилки ізолюються в окремих процесах, а система відновлюється через нагляд (supervision). AtomVM реалізує цю модель, дозволяючи ізолювати збої в Erlang-процесах, що є аналогом строгій обробці винятків в Ada, але з меншим накладними витратами.
Легке ядро: LK забезпечує мінімалістичне ядро з модульною архітектурою, що дозволяє адаптувати систему до конкретних потреб сертифікації, подібно до того, як Ada використовується з RTOS (наприклад, RTEMS). На відміну від Ada, LK+AtomVM не потребує складного рантайму, що спрощує сертифікацію для DO-178C чи подібних стандартів.
Конкурентність: Модель акторів Erlang, реалізована в AtomVM, забезпечує природну конкурентність і розподіленість, що є перевагою над Ada у сценаріях, де потрібна паралельна обробка (наприклад, у системах реального часу). LK додає базові потоки, які можна оптимізувати для цього.
Мінімальний розмір: Поєднання LK (15-20 КБ ядра) і AtomVM (100-200 КБ) дозволяє розмістити систему на пристроях із 256 КБ оперативної пам’яті, що значно менше, ніж типові вимоги Ada-систем із повноцінним RTOS.

Обмеження порівняно з Ada

Сертифікація: Ada має багаторічну історію сертифікації (наприклад, SPARK для формальної верифікації), тоді як LK+AtomVM потребуватиме додаткових зусиль для доведення відповідності стандартам безпеки (наприклад, аналізу коду LK і AtomVM).
Статична типізація: Ada забезпечує статичну перевірку типів на етапі компіляції, тоді як Erlang в AtomVM використовує динамічну типізацію, що може ускладнити формальний аналіз для сертифікації.
Інструментарій: Ada має розвинені інструменти (GNAT, AdaCore), тоді як LK+AtomVM потребуватиме створення спеціалізованих засобів для верифікації та тестування.

Потенційні сценарії використання

Критичні IoT-системи: LK+AtomVM може бути використано в розумних датчиках або медичних пристроях, де потрібна надійність і легка конкурентність, а розмір коду критичний.
Авіаційні підсистеми: Як альтернатива Ada в некритичних компонентах (наприклад, моніторинг), де простота і швидкий розвиток важливіші за повну сертифікацію.
Освітні та дослідницькі проекти: Для експериментів із надійними розподіленими системами, як запропоновано Армстронгом, LK+AtomVM є доступною платформою.

Висновок щодо альтернативи Ada

LK із AtomVM може слугувати перспективною альтернативою Ada для вбудованих систем, де потрібна надійність і легка обробка помилок, натхненна ідеями Джо Армстронга. Хоча повна заміна Ada в сертифікованих середовищах потребуватиме значних зусиль для верифікації та сертифікації, ця платформа ідеально підходить для менш суворих сценаріїв або як проміжний крок до сертифікованих рішень, пропонуючи баланс між простотою, ефективністю та стійкістю до помилок.

Можливість застосування як альтернативи L4

LK із AtomVM також може розглядатися як альтернатива мікроядру L4, яке відоме своєю мінімалістичною архітектурою та широким використанням у системах реального часу й безпечних вбудованих платформах. L4 забезпечує строгу ізоляцію процесів і низькі накладні витрати завдяки малому розміру ядра (близько 10-15 КБ), але потребує складної інтеграції користувацьких сервісів для повноцінної роботи. На відміну від L4, комбінація LK і AtomVM пропонує готове рішення з підтримкою конкурентності Erlang "з коробки", що спрощує розробку розподілених систем без необхідності створення додаткових компонентів, як у L4. LK забезпечує базову модульність і апаратну абстракцію, подібну до L4, але AtomVM додає високорівневу модель акторів, що робить платформу привабливою для застосувань, де потрібна швидка розробка надійних систем, наприклад, у телекомунікаціях або IoT. Хоча LK+AtomVM може поступатися L4 у швидкості міжпроцесної взаємодії (IPC) через меншу оптимізацію ядра, вона компенсує це легкістю використання та меншою складністю розгортання, що робить її перспективною для проєктів, де пріоритетом є простота і стійкість до помилок.

LK як завантажувач для NetBSD

Little Kernel (LK) може бути простим і ефективним завантажувачем для NetBSD завдяки своїй легкій і модульній архітектурі. NetBSD, відома своєю портативністю, працює на багатьох платформах, таких як ARM, RISC-V і x86, і потребує завантажувача для ініціалізації апаратного забезпечення та запуску ядра. LK ідеально підходить для цього, оскільки підтримує різноманітні архітектури і пропонує два режими управління пам’яттю: з MMU (через pmm і vmm) і без MMU (через novm). Це дозволяє адаптувати LK до будь-яких пристроїв, від простих мікроконтролерів до складніших систем. LK швидко ініціалізує апаратне забезпечення через двоетапний процес (vm_init_preheap і vm_init_postheap), забезпечуючи мінімальні накладні витрати, а його невеликий розмір (15-20 КБ) спрощує сертифікацію для критичних систем. Як завантажувач, LK може замінити традиційні рішення, такі як U-Boot, надаючи розробникам NetBSD гнучкість і простоту для створення надійного початкового етапу завантаження.